{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Introduction to atomman: Minimum energy paths\n",
    "\n",
    "__Lucas M. Hale__, [lucas.hale@nist.gov](mailto:lucas.hale@nist.gov?Subject=ipr-demo), _Materials Science and Engineering Division, NIST_.\n",
    "    \n",
    "[Disclaimers](http://www.nist.gov/public_affairs/disclaimer.cfm) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Introduction\n",
    "\n",
    "*New version 1.3.7*\n",
    "\n",
    "This Notebook outlines the mep subpackage of atomman that provides tools for identifying minimum energy pathways. These tools were designed primarily for finding relaxed energy paths along 2D energy maps, but may also work for more complex problems.\n",
    "\n",
    "__NOTE #1__ This documentation uses the term \"energy\" for the output of the evaluation function and the term \"force\" for the derivative of the evaluation function with respect to the coordinates.  This does not mean that the evaluation function needs to be in units of energy, just that it is a function analogous to energy.  \n",
    "\n",
    "__Note #2__ The atomman.mep module is relatively new and only contains a few options at the moment.  The code has been designed with modularity in mind, so additional methods can hopefully be added in the future without requiring too many changes to the existing design.  \n",
    "\n",
    "## 2. Theory and Methodology\n",
    "\n",
    "Minimum energy paths are transition paths from one (meta)stable state to another in which each point along the path is located at a minimum for all directions tangent to the path's line.  Another way of stating this is that every point along the path has a gradient of zero for all directions except along the path's line.  The minimum energy path for a transition is important as it marks the most likely states that the system will occupy during a transition and it provides an accurate means of identifying the transition barrier.\n",
    "\n",
    "The atomman.mep module contains Path classes.  Each Path class provides\n",
    "- A representation of a path along an energy surface as a series of coordinates,\n",
    "- Methods that allow for the integration of the path towards the minimum energy path using a particular relaxation algorithm, and\n",
    "- Tools for viewing the path, as well as the energies and forces along the path.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. BasePath class and common setup \n",
    "\n",
    "### 3.1. Path initialization \n",
    "\n",
    "All Path classes are children of BasePath, which defines the commonly shared methods and attributes.  \n",
    "\n",
    "A Path can be initialized with the following parameters\n",
    "\n",
    "- __coord__ (*array-like object*) The list of coordinates associated with the points along the path.\n",
    "- __energyfxn__ (*function*) The function that evaluates the energy associated with the different point coordinates.\n",
    "- __gradientfxn__ (*str or function, optional*) The function to use to estimate the gradient of the energy.  A str value of 'central_difference' or 'cdiff' (default) will use atomman.mep.gradient.central_difference.\n",
    "- __gradientkwargs__ (*dict, optional*) The keyword arguments (i.e. settings) to use wit the gradientfxn. Default is an empty dictionary, i.e. default settings of gradientfxn.\n",
    "- __integratorfxn__ (*str or function, optional*) The function to use to integrate relaxation steps.  A str value of euler will use atomman.mep.integrator.euler, while a str value of 'rungekutta' or 'rk' (default) will use atomman.mep.integrator.rungekutta.\n",
    "\n",
    "### 3.2. gradientfxn, gradientkwargs and integratorfxn\n",
    "\n",
    "The gradientfxn and gradientkwargs define the function to use for evaluating the gradient of coord.  The atomman.mep.gradient module collects the following gradient methods\n",
    "- __atomman.mep.gradient.central_difference(energyfxn, coord, shift=1e-5)__ computes the gradient of coord using central difference.  For each dimension, the energy is evaluated at coord$\\pm$shift and the gradient computed as the difference of the shifted energies divided by 2\\*shift.\n",
    "\n",
    "The integratorfxn defines the integration scheme to use to iterate coord forward by timestep in the direction given by the ratefxn to produce a new set of coord.  The atomman.mep.integrator module collects the following integration methods\n",
    "- __atomman.mep.integrator.euler(ratefxn, coord, timestep, \\*\\*kwargs)__ uses the Euler integration method\n",
    "    - newcoord = coord + timestep * ratefxn(\\*\\*kwargs)\n",
    "- __atomman.mep.integrator.rungekutta(ratefxn, coord, timestep, \\*\\*kwargs)__ uses the Runge-Kutta integration method, which uses a higher order estimate based on four shifts of coord.\n",
    "    - k1 = timestep * ratefxn(coord, **kwargs)\n",
    "    - k2 = timestep * ratefxn(coord - 0.5 * k1, **kwargs)\n",
    "    - k3 = timestep * ratefxn(coord - 0.5 * k2, **kwargs)\n",
    "    - k4 = timestep * ratefxn(coord - k3, \\*\\*kwargs)\n",
    "    - newcoord = coord + k1 / 6 + k2 / 3 + k3 / 3 + k4 / 6\n",
    "\n",
    "### 3.3. Common Path attributes\n",
    "\n",
    "- __coord__ (*numpy.NDArray*) is the array of coordinates along the path.\n",
    "- __arccoord__ (*numpy.NDArray*) computes the arc length coordinates for coord.  This is the cumulative sum of the radial distances between each set of neighboring points along the path.\n",
    "- __energyfxn__ (*function*) is the function to evaluate the energy associated with the coord points.\n",
    "- __gradientfxn__ (*function*) is the function for computing the energy gradient.\n",
    "- __gradientkwargs__ (*dict*) is a dict containing additional terms to pass to the gradientfxn.\n",
    "- __integratorfxn__ (*function*) is the function for iterating coord forward by a timestep.\n",
    "- __unittangent__ (*numpy.NDArray*)computes the unit tangent vector along the path at each point.  Note that this is not computed the same way for all subclasses.\n",
    "- __force__ (*numpy.NDArray*) computes the force associated with moving along the path at each coord.\n",
    "\n",
    "### 3.4. Common Path methods\n",
    "\n",
    "- __energy(coord=None)__ computes the energy using energyfxn for the set/given coord points.\n",
    "- __grad_energy(coord=None)__ computes the gradient of the energy using energyfxn and gradientfxn for the set/given coord points.\n",
    "- __step(\\*args, \\*\\*kwargs)__ returns a new Path object with coord evolved forward by one timestep.\n",
    "- __relax(\\*args, \\*\\*kwargs)__ performs multiple steps until a tolerance or a max number of steps is reached.  Returns a new Path object with the final coord.\n",
    "- __plot_energy(energy_unit=None, length_unit=None, ax=None, \\*\\*kwargs)__ plots the energy vs. arccoord for all points along the path. ax is an optional existing matplotlib.pyplot.axis object, allowing for subplots to be constructed.  All additional kwargs are passed on to matplotlib.pyplot.figure().  Note that energy_unit should correspond to the units returned by energyfxn, which may not be units of energy per se.\n",
    "- __plot_force(force_unit=None, length_unit=None, ax=None, \\*\\*kwargs)__ plots the force vs. arccoord for all points along the path. ax is an optional existing matplotlib.pyplot.axis object, allowing for subplots to be constructed.  All additional kwargs are passed on to matplotlib.pyplot.figure().  Note that force_unit should correspond to the units returned by energyfxn divided by length, which may not be units of force per se."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. ISMPath\n",
    "\n",
    "The ISMPath class uses the improved string method (ISM) [link?] to compute the minimum energy pathways. \n",
    "\n",
    "### 4.1. Improved string method theory\n",
    "\n",
    "In the improved string method, each relaxation step involves\n",
    "\n",
    "1. An initial path guess is given where all coordinates along the path are equally spaced according to the arc length coordinates.\n",
    "2. Each point is integrated by one time step along the negative gradient direction. \n",
    "3. The coordinates of the integrated points are then shifted such that they are once again equally spaced according to the arc length coordinates.  This is done by fitting a cubic spline interpolation relating arc length coordinates to the full coordinates of the relaxed points, then interpolating full coordinates associated with equally spaced arc length coordinates.\n",
    "\n",
    "When well behaved, step 2 relaxes the coordinates according to the gradient, and step 3 pulls the points back up to remain along the path.  \n",
    "\n",
    "After relaxing, a \"climbing\" algorithm can be used to better identify the critical barrier point along the path.  This follows the same basic concepts of the relaxation steps except that\n",
    "1. One or more climbing points are identified as having local maximum values after the relaxation steps.  \n",
    "2. The rate function for integrating the climbing points is modified from the negative gradient by reversing the gradient only along the unit tangent direction of the line path. This means that the climbing points will climb up the unit tangent direction rather than relax down.\n",
    "3. When applying the arc length adjustments, the full line path is broken into segments according to the climbing points.  The points within each segment are then adjusted to be equally arc length spaced with the end/climbing points not adjusted. \n",
    "\n",
    "### 4.2. ISMPath specific methods and attributes\n",
    "\n",
    "- __default_timestep__ (*float*) is the default relaxation timestep calculated as 0.05 * min(0.2, N^-1), where N is the number of coord points.\n",
    "- __default_tolerance__ (*float*) is the default relaxation tolerance calculated as max(N^-4, 1e-10), where N is the number of coord points.\n",
    "- __unittangent__ (*numpy.NDArray*) are the unit tangent vectors along the path line at each coord point.  For the ISM method, the tangent vector for the starting and ending points are calculated as the difference vector between those points and their neighbors, while the tangent vectors for all intermediate points are calculated as the difference vectors between their two neighboring points.\n",
    "- __interpolate_point(arccoord)__ constructs a cubic spline interpolation based on the current coord and corresponding arccoord values. The given arccoord parameter values are then used to interpolate new coord values.  The method returns a new ISMPath object with the interpolated coord values.\n",
    "\n",
    "#### 4.2.1. ISMPath.step()\n",
    "\n",
    "Performs a single string relaxation step.\n",
    "        \n",
    "Parameters\n",
    "        \n",
    "- __timestep__ (*float, optional*) The size of the timestep to use.  Will use the path's default timestep if not given.\n",
    "- __climbindex__ (*int, list or None, optional*) Indicates the indices of the path points to apply the climb algorithm to.  If None (default), no climbing will be performed.\n",
    "        \n",
    "Returns\n",
    "        \n",
    "- __newpath__ (*Path*) A Path with coordinates evolved forward by one timestep.\n",
    "\n",
    "#### 4.2.2. ISMPath.relax()\n",
    "\n",
    "Perform multiple relaxation and/or climb steps until either the maximum coordinate displacement per step drops below a tolerance or the maximum number of steps is reached.\n",
    "        \n",
    "Parameters\n",
    "        \n",
    "- __relaxsteps__ (*int, optional*) The maximum number of relaxation steps to perform.  Default value is 0: no relaxation steps.\n",
    "- __climbsteps__ (*int, optional*) The maximum number of climbing steps to perform.  Default value is 0: no climbing steps.\n",
    "- __timestep__ (*float, optional*) The size of the timestep to use.  Will use default_timestep if not given.\n",
    "- __tolerance__ (*float, optional*) The coordinate displacement tolerance to use.  Will use default_tolerance if not given.\n",
    "- __climbpoints__ (*int, optional*) Indicates the maximum number of points to subject the climbing to. Default value is 1: i.e. only one maximum is refined.\n",
    "- __verbose__ (*bool, optional*) If True (default), informative statements about the relaxation are printed.\n",
    "        \n",
    "Returns\n",
    "        \n",
    "- __newpath__ (*Path*) A Path with coordinates evolved by the relaxation process."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
